package cn.gowss.vnc.ws;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketFrame;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.server.WebSocketHandler;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

@WebSocket
public class WsHandler extends WebSocketHandler {

    private Socket vncSocket;
    private Session session;
    private static Executor vncExecutor = vncExecutor();
//    private static Map<String, Thread> vncThreadMap = new HashMap<String, Thread>(100);

//    int vncPort = 0;
//    String vncServer = "";

    @Override
    public void configure(WebSocketServletFactory webSocketServletFactory) {
        webSocketServletFactory.register(WsHandler.class);
    }

    @Override
    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
    	if(this.getWebSocketFactory().isUpgradeRequest(request, response)) {
            response.addHeader("Sec-WebSocket-Protocol", "binary");
            if(this.getWebSocketFactory().acceptWebSocket(request, response)) {
                baseRequest.setHandled(true);
                return;
            }

            if(response.isCommitted()) {
                return;
            }
        }

        super.handle(target, baseRequest, request, response);
    }

    @OnWebSocketConnect
    public void onConnect(final Session session) throws IOException, InterruptedException {
        System.out.println("Connect: " + session.getRemoteAddress().getAddress());
        System.out.println(session.getUpgradeRequest().getRequestURI());
        System.out.println(session.getUpgradeRequest().getParameterMap());
        this.session = session;
        Map<String, List<String>> parameterMap = session.getUpgradeRequest().getParameterMap();
        String vncserver = parameterMap.get("vncserver").get(0);
        String server = "";
        int port = 5901;
    	if(StringUtil.isNotBlank(vncserver)) {
    		server = vncserver;
    	}
    	String vncport = parameterMap.get("vncport").get(0);
    	if(StringUtil.isNotBlank(vncport)) {
    		port = Integer.valueOf(vncport);
    	}
//        vncSocket = socketMap.get(server);
//    	if(vncSocket == null){
//            vncSocket = new Socket(server, port);
//            socketMap.put(server, vncSocket);
//        }
        vncSocket = new Socket(server, port);
        Thread vncThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    byte[] b = new byte[1500];
                    int readBytes;
                    while (true){
                        readBytes = vncSocket.getInputStream().read(b);
                        if (readBytes == -1){
                            break;
                        }
                        if (readBytes > 0) {
                            session.getRemote().sendBytes(ByteBuffer.wrap(b,0, readBytes));
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        vncThread.start();
//        vncThreadMap.put(server, vncThread);
        /*vncExecutor.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    byte[] b = new byte[1500];
                    int readBytes;
                    while (true){
                        readBytes = vncSocket.getInputStream().read(b);
                        if (readBytes == -1){
                            break;
                        }
                        if (readBytes > 0) {
                            session.getRemote().sendBytes(ByteBuffer.wrap(b,0, readBytes));
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });*/
    }

    @OnWebSocketFrame
    public void onFrame(Frame f) throws IOException {
        byte[] data = new byte[f.getPayloadLength()];
        f.getPayload().get(data);
        vncSocket.getOutputStream().write(data);
    }

    @OnWebSocketClose
    public void onClose(int statusCode, String reason) throws IOException {
        System.out.printf("客户端关闭: %d - %s%n",statusCode,reason);
        this.session = null;
        //this.closeLatch.countDown(); // 触发位置
        String hostAddress = this.vncSocket.getInetAddress().getHostAddress();
        this.vncSocket.close();
        System.out.printf("socket已关闭，socket连接地址：%s%n", hostAddress);
        /*Thread thread = vncThreadMap.get(hostAddress);
        vncThreadMap.remove(hostAddress);
        if(!thread.isInterrupted()){
        	thread.interrupt();
        	try {
				thread.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
        	System.out.printf("线程已经手动终止，socket连接地址：%s%n", hostAddress);
        }else{
        	System.out.printf("线程已经自动终止，socket连接地址：%s%n", hostAddress);
        }*/
    }

    public static Executor vncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(100);
        //配置最大线程数
        executor.setMaxPoolSize(200);
        // 设置队列容量
        executor.setQueueCapacity(1000);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix("vnc-service-");

        // rejection-policy：当pool已经达到max size的时候，如何处理新任务
        // CALLER_RUNS：不在新线程中执行任务，而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
        return executor;
    }
}
